<template>
  <div class="login-container">
    <div id="login-three-container"></div>
    <div class="login-plane">
      <div class="login-plane-container">
        <img
          class="login-plane-human"
          src="@/assets/images/guologo2.png"
          alt=""
        />
        <div class="login-plane-title">
          研究一部
          <img
            class="login-plane-title-line"
            src="@/assets/images/login_horizontal_line.png"
            alt=""
          />
        </div>
        <div class="login-plane-form">
          <el-form :model="formField" :rules="formRules" ref="formRef">
            <el-form-item prop="user">
              <el-input
                placeholder="用户名 / 账号"
                type="text"
                v-model="formField.user"
              ></el-input>
            </el-form-item>
            <el-form-item prop="pass">
              <el-input
                placeholder="密码"
                type="password"
                v-model="formField.pass"
                @keyup.enter.native="handleLogin"
              ></el-input>
            </el-form-item>
          </el-form>
          <el-button
            @click="handleLogin"
            style="width: 100%"
            type="primary"
            @click.native.prevent="handleLogin"
            >登录</el-button
          >
        </div>
      </div>
    </div>
    <div class="login-ground"></div>
  </div>
</template>

<script>
import { validUsername } from "@/utils/validate";
import * as THREE from "three";
import { GUI } from "three/examples/jsm/libs/dat.gui.module";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import Stats from "three/examples/jsm/libs/stats.module.js";
import _ from "lodash";
import { sha256 } from "js-sha256";
export default {
  name: "HelloWorld",

  data() {
    const validateUsername = (rule, value, callback) => {
      if (!validUsername(value)) {
        callback(new Error("请输入正确用户名"));
      } else {
        callback();
      }
    };
    const validatePassword = (rule, value, callback) => {
      if (this.formField.user == "yanjiuyibu") {
        if (
          sha256(value) !=
          "207d1398f6c96ca1b11910976bcaaa0d3b82f53ea69c1f388c3c8176d3cc0c3b" //
        ) {
          callback(new Error("请输入正确密码"));
        } else {
          callback();
        }
      } else if (this.formField.user == "news") {
        if (
          sha256(value) !=
          "f206189c5c46fd4f7f762ae6bf5ec812420dd51ffcd56f80b0135dfa0bea7ecf" //f206189c5c46fd4f7f762ae6bf5ec812420dd51ffcd56f80b0135dfa0bea7ecf
        ) {
          callback(new Error("请输入正确密码"));
        } else {
          callback();
        }
      } else {
        callback();
      }
    };
    return {
      // 容器
      container: "",
      // 声明视口宽度
      width: 0,
      // 声明视口高度
      height: 0,
      // 盒模型的深度
      depth: 1400,
      // 声明场景
      scene: {},
      // 声明球组
      Sphere_Group: "",
      // 声明球体几何
      sphereGeometry: "",
      // 声明完整球
      sphere: "",
      // 声明相机
      camera: "",
      // 声明相机在z轴的位置
      zAxisNumber: 0,
      // 声明相机目标点
      cameraTarget: new THREE.Vector3(0, 0, 0),
      // 声明点材质
      materials: [],
      // 声明点的参数
      parameters: "",
      // 声明点在z轴上移动的进度
      zprogress: 0,
      // 声明同上（第二个几何点）
      zprogress_second: 0,
      // 声明粒子1
      particles_first: [],
      // 声明粒子1
      particles_second: [],
      // 声明粒子1的初始化位置
      particles_init_position: 0,
      // 声明流动的云对象1（包含路径、云实例）
      cloudParameter_first: "",
      // 声明流动的云对象2（包含路径、云实例）
      cloudParameter_second: "",
      // 声明云流动的渲染函数1
      renderCloudMove_first: "",
      // 声明云流动的渲染函数1
      renderCloudMove_second: "",
      // 声明性能监控
      stats: new Stats(),
      // 声明渲染器
      renderer: "",
      // 声明调试工具
      // gui: new GUI(),

      // 表单对象
      formRef: null,

      // 其他状态
      state: {
        codeSrc: "",
        codetoken: "",
      },
      formField: {
        user: "",
        pass: "",
        code: "",
      },
      // 表单校验规则
      formRules: {
        user: [
          {
            required: true,
            message: "请输入用户名账号",
            trigger: "blur",
            validator: validateUsername,
          },
        ],
        pass: [
          {
            required: true,
            message: "请输入密码",
            trigger: "blur",
            validator: validatePassword,
          },
        ],
      },
    };
  },
  mounted() {
    this.container = document.getElementById("login-three-container");
    this.width = this.container.clientWidth;
    this.height = this.container.clientHeight;
    this.initScene();
    this.initSceneBg();
    this.initCamera();
    this.initLight();
    this.initSphereModal();
    this.initSphereGroup();
    this.particles_init_position = -this.zAxisNumber - this.depth / 2;
    this.zprogress = this.particles_init_position;
    this.zprogress_second = this.particles_init_position * 2;
    this.particles_first = this.initSceneStar(this.particles_init_position);
    this.particles_second = this.initSceneStar(this.zprogress_second);
    this.cloudParameter_first = this.initTubeRoute(
      [
        new THREE.Vector3(-this.width / 10, 0, -this.depth / 2),
        new THREE.Vector3(-this.width / 4, this.height / 8, 0),
        new THREE.Vector3(-this.width / 4, 0, this.zAxisNumber),
      ],
      400,
      200
    );
    this.cloudParameter_second = this.initTubeRoute(
      [
        new THREE.Vector3(this.width / 8, this.height / 8, -this.depth / 2),
        new THREE.Vector3(this.width / 8, this.height / 8, this.zAxisNumber),
      ],
      200,
      100
    );
    this.initRenderer();
    // 控制器必须放在renderer函数后面
    this.initOrbitControls();
    this.animate();
    // initGUI()
    // const axesHelper = new THREE.AxesHelper(2000)
    // scene.add(axesHelper)},
  },
  methods: {
    // 初始化场景
    initScene() {
      this.scene = new THREE.Scene();
      // 在场景中添加雾的效果，Fog参数分别代表‘雾的颜色’、‘开始雾化的视线距离’、刚好雾化至看不见的视线距离’
      this.scene.fog = new THREE.Fog(0x000000, 0, 10000);
    },
    // 初始化背景（盒模型背景，视角在盒子里面，看到的是盒子内部）
    initSceneBg() {
      new THREE.TextureLoader().load(
        require("@/assets/images/sky.png"),
        (texture) => {
          const geometry = new THREE.BoxGeometry(
            this.width,
            this.height,
            this.depth
          ); // 创建一个球形几何体 SphereGeometry
          const material = new THREE.MeshBasicMaterial({
            map: texture,
            side: THREE.BackSide,
          }); // 创建基础为网格基础材料
          const mesh = new THREE.Mesh(geometry, material);
          this.scene.add(mesh);
        }
      );
    },
    // 初始化轨道控制器
    initOrbitControls() {
      const controls = new OrbitControls(this.camera, this.renderer.domElement);
      // enabled设置为true是可以使用鼠标控制视角
      controls.enabled = true;
      controls.update();
    },
    // 初始化相机
    initCamera() {
      /**
       * 方式1：固定视野的距离，求满足完整的视野画面需要多大的视域角度
       * tan正切值（直角边除以临边）
       * const mathTan_value = width / 2 / depth
       * 视域角度
       * const fov_angle = (Math.atan(mathTan_value) * 180) / Math.PI
       * 创建透视相机
       * new THREE.PerspectiveCamera(fov_angle, width / height, 1, depth)
       * 场景是一个矩形容器（坐标(0, 0, 0)是矩形容器的中心），相机能看到的距离是depth，
       * camera.position.set(0, 0, depth / 2)
       */

      /**
       * 使用透视相机
       * 参数值分别是：
       * fov（field of view） — 摄像机视锥体垂直视野角度
       * aspect — 摄像机视锥体长宽比
       * near — 摄像机视锥体近端面
       * far — 摄像机视锥体远端面
       * 这里需要注意：透视相机是鱼眼效果，如果视域越大，边缘变形越大。
       * 为了避免边缘变形，可以将fov角度设置小一些，距离拉远一些
       */

      /**
       * 方式2:固定视域角度，求需要多少距离才能满足完整的视野画面
       * 15度等于(Math.PI / 12)
       */
      const fov = 15;
      const distance = this.width / 2 / Math.tan(Math.PI / 12);
      this.zAxisNumber = Math.floor(distance - this.depth / 2);
      this.camera = new THREE.PerspectiveCamera(
        fov,
        this.width / this.height,
        1,
        30000
      );
      /**
       * 这里给z轴的距离加了100，原因是做调整，使得视域更完整
       * 这么做并不代表前面计算错误了，根据前面的计算值并不能很完整的看到
       * 至于原因，我想大概就类似于0.1+0.2不等于0.3吧
       * 所以我自作主张地加了100的值做调整（但是不建议，因为当屏幕足够宽时候会看到边缘）
       */
      // camera.position.set(0, 0, zAxisNumber + 100)
      this.camera.position.set(0, 0, this.zAxisNumber);
      this.camera.lookAt(this.cameraTarget);
      // const helper = new THREE.CameraHelper(camera)
      // helper.update()
      // scene.add(helper)
    },
    //光源
    initLight() {
      const ambientLight = new THREE.AmbientLight(0xffffff, 1);
      // 右下角点光源
      const light_rightBottom = new THREE.PointLight(0x0655fd, 5, 0);
      light_rightBottom.position.set(0, 100, -200);
      this.scene.add(light_rightBottom);
      this.scene.add(ambientLight);
    },
    // 初始化球体模型
    initSphereModal() {
      //材质
      let material = new THREE.MeshPhongMaterial();
      material.map = new THREE.TextureLoader().load(
        require("@/assets/images/earth_bg.png")
      );
      material.blendDstAlpha = 1;
      //几何体
      this.sphereGeometry = new THREE.SphereGeometry(50, 64, 32);
      //模型
      this.sphere = new THREE.Mesh(this.sphereGeometry, material);
    },
    // 初始化组 --- 球体
    initSphereGroup() {
      this.Sphere_Group = new THREE.Group();
      this.Sphere_Group.add(this.sphere);
      this.Sphere_Group.position.x = -400;
      this.Sphere_Group.position.y = 200;
      this.Sphere_Group.position.z = -200;
      this.scene.add(this.Sphere_Group);
    },
    // 初始化流动路径
    initTubeRoute(route, geometryWidth, geometryHeigh) {
      const curve = new THREE.CatmullRomCurve3(route, false);
      const tubeGeometry = new THREE.TubeGeometry(curve, 100, 2, 50, false);
      const tubeMaterial = new THREE.MeshBasicMaterial({
        // color: '0x4488ff',
        opacity: 0,
        transparent: true,
      });
      const tube = new THREE.Mesh(tubeGeometry, tubeMaterial);
      this.scene.add(tube);

      const clondGeometry = new THREE.PlaneGeometry(
        this.geometryWidth,
        this.geometryHeigh
      );
      const textureLoader = new THREE.TextureLoader();
      const cloudTexture = textureLoader.load(
        require("@/assets/images/cloud.png")
      );
      const clondMaterial = new THREE.MeshBasicMaterial({
        map: cloudTexture,
        blending: THREE.AdditiveBlending,
        depthTest: false,
        transparent: true,
      });
      const cloud = new THREE.Mesh(clondGeometry, clondMaterial);
      this.scene.add(cloud);
      return {
        cloud,
        curve,
      };
    },
    // 初始化场景星星效果
    initSceneStar(initZposition) {
      const geometry = new THREE.BufferGeometry();
      const vertices = [];
      const pointsGeometry = [];
      const textureLoader = new THREE.TextureLoader();
      const sprite1 = textureLoader.load(
        require("@/assets/images/starflake1.png")
      );
      const sprite2 = textureLoader.load(
        require("@/assets/images/starflake2.png")
      );
      this.parameters = [
        [[0.6, 100, 0.75], sprite1, 50],
        [[0, 0, 1], sprite2, 20],
      ];
      // 初始化500个节点
      for (let i = 0; i < 500; i++) {
        /**
         * const x: number = Math.random() * 2 * width - width
         * 等价
         * THREE.MathUtils.randFloatSpread(width)
         */
        const x = THREE.MathUtils.randFloatSpread(this.width);
        const y = _.random(0, this.height / 2);
        const z = _.random(-this.depth / 2, this.zAxisNumber);
        vertices.push(x, y, z);
      }

      geometry.setAttribute(
        "position",
        new THREE.Float32BufferAttribute(vertices, 3)
      );

      // 创建2种不同的材质的节点（500 * 2）
      for (let i = 0; i < this.parameters.length; i++) {
        const color = this.parameters[i][0];
        const sprite = this.parameters[i][1];
        const size = this.parameters[i][2];

        this.materials[i] = new THREE.PointsMaterial({
          size,
          map: sprite,
          blending: THREE.AdditiveBlending,
          depthTest: true,
          transparent: true,
        });
        this.materials[i].color.setHSL(color[0], color[1], color[2]);
        const particles = new THREE.Points(geometry, this.materials[i]);
        particles.rotation.x = Math.random() * 0.2 - 0.15;
        particles.rotation.z = Math.random() * 0.2 - 0.15;
        particles.rotation.y = Math.random() * 0.2 - 0.15;
        particles.position.setZ(initZposition);
        pointsGeometry.push(particles);
        this.scene.add(particles);
      }
      return pointsGeometry;
    },
    // 渲染星球的自转
    renderSphereRotate() {
      if (this.sphere) {
        this.Sphere_Group.rotateY(0.001);
      }
    },
    // 渲染星星的运动
    renderStarMove() {
      const time = Date.now() * 0.00005;
      this.zprogress += 1;
      this.zprogress_second += 1;

      if (this.zprogress >= this.zAxisNumber + this.depth / 2) {
        this.zprogress = this.particles_init_position;
      } else {
        this.particles_first.forEach((item) => {
          item.position.setZ(this.zprogress);
        });
      }
      if (this.zprogress_second >= this.zAxisNumber + this.depth / 2) {
        this.zprogress_second = this.particles_init_position;
      } else {
        this.particles_second.forEach((item) => {
          item.position.setZ(this.zprogress_second);
        });
      }

      for (let i = 0; i < this.materials.length; i++) {
        const color = this.parameters[i][0];

        const h = ((360 * (color[0] + time)) % 360) / 360;
        this.materials[i].color.setHSL(
          color[0],
          color[1],
          parseFloat(h.toFixed(2))
        );
      }
    },
    // 初始化云的运动函数
    initCloudMove(
      cloudParameter,
      speed,
      scaleSpeed = 0.0006,
      maxScale = 1,
      startScale = 0
    ) {
      let cloudProgress = 0;
      return () => {
        if (startScale < maxScale) {
          startScale += scaleSpeed;
          cloudParameter.cloud.scale.setScalar(startScale);
        }
        if (cloudProgress > 1) {
          cloudProgress = 0;
          startScale = 0;
        } else {
          cloudProgress += speed;
          if (cloudParameter.curve) {
            const point = cloudParameter.curve.getPoint(cloudProgress);
            if (point && point.x) {
              cloudParameter.cloud.position.set(point.x, point.y, point.z);
            }
          }
        }
      };
    },
    //渲染器
    initRenderer() {
      // 开启抗锯齿
      // 在 css 中设置背景色透明显示渐变色
      this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
      // 定义渲染器的尺寸；在这里它会填满整个屏幕
      this.renderer.setSize(this.width, this.height);
      this.renderer.shadowMap.enabled = true;
      this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
      this.container.appendChild(this.renderer.domElement);
      // this.container.appendChild(this.stats.dom);
      this.renderCloudMove_first = this.initCloudMove(
        this.cloudParameter_first,
        0.0002
      );
      this.renderCloudMove_second = this.initCloudMove(
        this.cloudParameter_second,
        0.0008,
        0.001
      );
    },
    //动画刷新
    animate() {
      requestAnimationFrame(this.animate);
      this.renderSphereRotate();
      this.renderStarMove();
      this.renderCloudMove_first();
      this.renderCloudMove_second();
      this.renderer.render(this.scene, this.camera);
    },
    Params() {
      this.color = "#000";
      this.length = 10;
      this.size = 3;
      this.visible = true;
      this.x = 0;
      this.y = 0;
      this.z = 100;
      this.widthSegments = 64;
      this.heightSegments = 32;
      this.radius = 16;
    },
    // 初始化gui
    initGUI() {
      const params = new Params();
      console.log(params);
      gui.add(params, "x", -1500, 1500).onChange((x) => {
        //点击颜色面板，e为返回的10进制颜色
        Sphere_Group.position.x = x;
      });
      gui.add(params, "y", -50, 1500).onChange((y) => {
        //点击颜色面板，e为返回的10进制颜色
        Sphere_Group.position.y = y;
      });
      gui.add(params, "z", -200, 1000).onChange((z) => {
        //点击颜色面板，e为返回的10进制颜色
        Sphere_Group.position.z = z;
      });
      gui.add(params, "widthSegments", 0, 64).onChange((widthSegments) => {
        //点击颜色面板，e为返回的10进制颜色
        sphereGeometry.parameters.widthSegments = widthSegments;
      });
      gui.add(params, "heightSegments", 0, 32).onChange((heightSegmentsr) => {
        //点击颜色面板，e为返回的10进制颜色
        sphereGeometry.parameters.heightSegments = heightSegments;
      });
      gui.add(params, "radius", 5, 30).onChange((radius) => {
        //点击颜色面板，e为返回的10进制颜色
        sphereGeometry.parameters.radius = radius;
        renderer.render(this.scene, this.camera);
      });
      gui.add(params, "visible").onChange((e) => {
        //这是一个单选框，因为params.visible是一个布尔值，e返回所选布尔值
        // points.visible = e
      });
      gui.addColor(params, "color").onChange((e) => {
        //点击颜色面板，e为返回的10进制颜色
        // pointsMaterial.color.set(e)
      });
    },
    async getValidateCodeHandle() {
      // 请求获取验证码 并设置验证码的图片以及验证码token
      this.state.codeSrc = "";
      this.state.codetoken = "";
    },
    // 提交表单
    submitForm() {
      const form = this.formRef;
      if (!form) return;
      form.validate((valid) => {
        if (valid) {
          this.submitHandle();
        } else {
          this.$message({
            message: "随便输入用户名、密码、验证码即可登陆",
            type: "warning",
          });
        }
      });
      // 提交请求
      submitHandle = async () => {
        const params = {
          password: formField.pass,
          username: formField.user,
          verifyCode: formField.code,
        };
        // 提交登陆请求
      };
    },
    handleLogin() {
      // this.$refs.formRef.validate((valid) => {
      //   if (valid) {
      //     // sessionStorage.setItem("autu", true);
      //     localStorage.setItem("autu", true);
      //     this.$router.push({ path: "/routeArticle" });
      //   } else {
      //     this.$message({ type: "error", message: "请输入正确用户信息" });
      //     return false;
      //   }
      // });
      this.$router.push({ path: "/routeArticle" });
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
.login-container {
  width: 100%;
  height: 100vh;
  position: relative;
  #login-three-container {
    width: 100%;
    height: 100%;
  }
  .login-plane {
    position: absolute;
    z-index: 9999;
    width: 500px;
    height: 400px;
    background-image: url("~@/assets/images/login_border.png");
    background-repeat: no-repeat;
    background-size: 100% 100%;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    .login-plane-container {
      width: 100%;
      height: 100%;
      border-radius: 18px;
      background-color: #007eff2e;
      position: relative;
      @keyframes humanMove {
        0% {
          top: -100px;
        }

        50% {
          top: -99px;
        }

        100% {
          top: -100px;
        }
      }
      .login-plane-human {
        position: absolute;
        width: 260px;
        right: -120px;
        top: -100px;
        animation: humanMove 10s linear 0s infinite normal;
      }
      .login-plane-title {
        width: 100%;
        height: 100px;
        display: flex;
        align-items: center;
        justify-content: center;
        position: relative;
        font-size: 35px;
        color: #fff;
        font-weight: 700;
        img {
          width: 50%;
        }
        .login-plane-title-line {
          width: 80%;
          position: absolute;
          bottom: 0;
        }
      }
      .login-plane-form {
        padding: 45px 55px;
        box-sizing: border-box;
        .login-code-container {
          display: flex;
          align-items: flex-start;
          justify-content: space-between;
          .login-code {
            cursor: pointer;
            width: 45%;
            height: 40px;
            background-color: #c8c8c8;
            img {
              width: 100%;
              height: 100%;
            }
          }
        }
      }
    }
  }
  .login-ground {
    position: absolute;
    z-index: 9998;
    width: 100%;
    height: 400px;
    background-image: url("~@/assets/images/ground.png");
    background-repeat: no-repeat;
    background-size: 100% 100%;
    bottom: 0;
    left: 0;
  }
}
</style>
